package com.lianlianche.arch.jetpack.base.paging

import androidx.lifecycle.MutableLiveData
import androidx.paging.PageKeyedDataSource
import com.lianlianche.arch.jetpack.ktx.toMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber

/**
 * <pre>
 *     @author : Mid
 *     e-mail  : boyce903301689@gmail.com
 *     time    : 2019/8/13
 *     desc    : 基础的数据源
 *     version : 1.0
 * </pre>
 */
abstract class BasePageKeyedDataSource<Key, Value>(private val scope: CoroutineScope) : PageKeyedDataSource<Key, Value>() {

    private val TAG = javaClass.simpleName

    // keep a function reference for the retry event
    private var retry: (() -> Any)? = null

    /**
     * There is no sync on the state because paging will always call loadInitial first then wait
     * for it to return some success value before calling loadAfter.
     */
    val loadAfterState = MutableLiveData<NetworkState>()
    val loadInitialState = MutableLiveData<NetworkState>()


    /**
     * 拉取数据的时候可以伴有界面过渡效果
     */
    protected fun initialOf(params: LoadInitialParams<Key>, callback: LoadInitialCallback<Key, Value>, block: suspend () -> Unit) = scope.launch {
        Timber.w(TAG, "initialOf ")
        loadInitialState.postValue(NetworkState.LOADING)
        try {
            block()
            loadInitialState.postValue(NetworkState.LOADED)
        } catch (t: Throwable) {
            t.printStackTrace()
            retry = { loadInitial(params, callback) }
            loadInitialState.postValue(NetworkState.error(t.toMessage()))
        }
    }


    /**
     * 加载更多数据的时候伴有界面过渡效果
     */
    protected fun afterOf(params: LoadParams<Key>, callback: LoadCallback<Key, Value>, block: suspend () -> Unit) = scope.launch {
        Timber.w(TAG, "afterOf currentKey: ${params.key}")
        loadAfterState.postValue(NetworkState.LOADING)
        try {
            block()
            loadAfterState.postValue(NetworkState.LOADED)
        } catch (t: Throwable) {
            t.printStackTrace()
            retry = { loadBefore(params, callback) }
            loadAfterState.postValue(NetworkState.error(t.toMessage()))
        }
    }


    /**
     * 重试数据加载失败
     */
    fun retryAllFailed() {
        val prevRetry = retry
        retry = null
        prevRetry?.invoke()
    }

}